/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.pb138;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;

/**
 *
 * @author petri_000
 */
@WebServlet(name = "VisualFireBrnoServlet", urlPatterns = {"/VisualFireBrnoServlet/*"})
public class VisualFireBrnoServlet extends HttpServlet {

    public static final int NUMBER_OF_PAGES = 11;
    private Map<String, Integer> months = new HashMap<>();
    private static final Logger logger = Logger.getLogger(VisualFireBrnoServlet.class.getName());
    private FileHandler fh = null;
    /**
     * Processes requests for both HTTP
     * <code>GET</code> and
     * <code>POST</code> methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        setMonths();
        setLoggerHandler();

        try { 
            Document doc = parseFiresFromFireBrno();
            if (doc != null) {
                transformDOMtoFile(doc);
                logger.fine("New database created");
            }
        }  catch (TransformerConfigurationException ex) {
            logger.log(Level.SEVERE, "Failed to create transformer instance", ex);
        } catch (TransformerException ex) {
            logger.log(Level.SEVERE, "Failed to transform Document to file", ex);
        } catch (Exception ex) {
            logger.log(Level.SEVERE, "Failed to load new data, using old database", ex);
        } finally {
            fh.close();
            request.getRequestDispatcher("/index.jsp").forward(request, response);
        }
    }

    /**
     * Maps czech month names to equivalent numbers
     */
    private void setMonths() {
        months.put("leden", 1);
        months.put("únor", 2);
        months.put("březen", 3);
        months.put("duben", 4);
        months.put("květen", 5);
        months.put("červen", 6);
        months.put("červenec", 7);
        months.put("srpen", 8);
        months.put("září", 9);
        months.put("říjen", 10);
        months.put("listopad", 11);
        months.put("prosinec", 12);
    }

    /**
     * Transforms DOM to file
     *
     * @param doc DOM source object
     */
    private void transformDOMtoFile(Document doc) throws TransformerConfigurationException, TransformerException {
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        Result output = new StreamResult(new File(getServletContext().getRealPath("/") + "fires.xml"));
        Source input = new DOMSource(doc);
        transformer.transform(input, output);
    }

    /**
     * Parses details about fires from firebrno.cz
     *
     * @return DOM with fire details
     */
    private Document parseFiresFromFireBrno() {
        Document outdoc = null;

        try {
            Tidy tidy = new Tidy();
            tidy.setXHTML(true);
            tidy.setShowWarnings(false);
            tidy.setErrfile("tidyout.txt");
            tidy.setInputEncoding("UTF-8");
            tidy.setOutputEncoding("UTF-8");
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            outdoc = docBuilder.newDocument();
            Element root = outdoc.createElement("fires");
            root.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance",
                    "xsi:noNamespaceSchemaLocation", "fires.xsd");
            outdoc.appendChild(root);

            for (int page = 1; page < NUMBER_OF_PAGES; page++) {
                URL u = new URL("http://www.firebrno.cz/modules/incidents/index.php?filter%5Bdate%5D=&filter%5Btype_id%5D=1&filter%5Bdistrict_id%5D=3702&search=Vyhledat&page=" + String.valueOf(page));

                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                Document doc = tidy.parseDOM(u.openStream(), outputStream);
                XPathFactory xPathfactory = XPathFactory.newInstance();
                XPath xpath = xPathfactory.newXPath();
                XPathExpression expr = xpath.compile("//*[@id=\"main-content-center\"]/div/div/div[2]/div/h3/a/@href");
                NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);


                int nlc = nl.getLength();

                for (int i = 0; i < nlc; i++) {
                    Element fire = outdoc.createElement("fire");
                    root.appendChild(fire);
                    //System.out.println("http://www.firebrno.cz/modules/incidents/" + nl.item(i).getNodeValue());


                    URL detailurl = new URL("http://www.firebrno.cz/modules/incidents/" + nl.item(i).getNodeValue());
                    ByteArrayOutputStream doutputStream = new ByteArrayOutputStream();
                    Document detaildoc = tidy.parseDOM(detailurl.openStream(), doutputStream);

                    expr = xpath.compile("//*[@id=\"main-content-center\"]/div[1]/div/div[2]/p[strong]/text()");
                    NodeList detailnode = (NodeList) expr.evaluate(detaildoc, XPathConstants.NODESET);
                    int posun=0;
                    if (detailnode.getLength()==7) posun=1;
                    Element street = outdoc.createElement("street");
                    street.appendChild(outdoc.createTextNode(detailnode.item(4+posun).getNodeValue().substring(2)));
                    fire.appendChild(street);


                    Element district = outdoc.createElement("district");
                    district.appendChild(outdoc.createTextNode(detailnode.item(3+posun).getNodeValue().substring(2)));
                    fire.appendChild(district);

                    Element desc = outdoc.createElement("description");
                    if (posun==1) desc.appendChild(outdoc.createTextNode(detailnode.item(1).getNodeValue().substring(2)));
                    fire.appendChild(desc);

                    // //*[@id="main-content-center"]/div[1]/div/div[1]/div[1]

                    expr = xpath.compile("//*[@id=\"main-content-center\"]/div[1]/div/div[1]/div[1]/text()");
                    Node datenode = (Node) expr.evaluate(detaildoc, XPathConstants.NODE);

                    String pattern = "^(\\d+)\\. (.*) (\\d{4}), (.*)$";

                    Element date = outdoc.createElement("date");
                    fire.appendChild(date);
                    Element day = outdoc.createElement("day");
                    Element month = outdoc.createElement("month");
                    Element year = outdoc.createElement("year");
                    Element time = outdoc.createElement("time");
                    day.appendChild(outdoc.createTextNode(datenode.getNodeValue().replaceAll(pattern, "$1")));
                    month.appendChild(outdoc.createTextNode(months.get(datenode.getNodeValue().replaceAll(pattern, "$2")).toString()));
                    year.appendChild(outdoc.createTextNode(datenode.getNodeValue().replaceAll(pattern, "$3")));
                    time.appendChild(outdoc.createTextNode(datenode.getNodeValue().replaceAll(pattern, "$4")));
                    date.appendChild(day);
                    date.appendChild(month);
                    date.appendChild(year);
                    date.appendChild(time);
                }
            }
        } catch (ParserConfigurationException ex) {
            logger.log(Level.SEVERE, "Failed to create document builder", ex);
        } catch (XPathExpressionException ex) {
            logger.log(Level.SEVERE, "XPath expression couldn't be evaluated", ex);
        } catch (MalformedURLException ex) {
            logger.log(Level.SEVERE, "Protocol not recognized", ex);
        } catch (IOException ex) {
            logger.log(Level.SEVERE, "jTidy failed to parse", ex);
        }

        return outdoc;
    }
    
    /**
     * Sets logger handler
     */
    private void setLoggerHandler() {
        try {
            fh = new FileHandler(getServletContext().getRealPath("/") + "VisualFireBrno.log", true);
            logger.addHandler(fh);
            logger.setLevel(Level.ALL);
            SimpleFormatter formatter = new SimpleFormatter();
            fh.setFormatter(formatter);
        } catch (SecurityException | IOException ex) {
            if (fh != null) {
                fh.close();
            }
            logger.removeHandler(fh);
        }
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP
     * <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP
     * <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

    
}
